package trabajoPracticoCalculadora;

public class Calculadora {
	private Pila<String> expresionPila;
	private Double resultado = 0.0;

	/**
	 * post: Inicializa Calculadora
	 */
	public Calculadora() {
		this.expresionPila = new Pila<String>();
		System.out.println("[Warn@constructor]**Calculadora Iniciada**");
	}

	/**
	 * Pre: Ingresa Vector String con la expresion a calcular Post: almacena
	 * resultado de dicho calculo
	 * 
	 * @param expresion
	 *            : vector de strings.
	 */
	public void evaluar(String[] expresion) {
		double operando = 0.0;

		for (int contador = 0; contador <= expresion.length; contador++) {
			// Arma Estructura Algebraica con Parentesis
			if (contador == 0)
				this.expresionPila.apilar("(");
			if (contador < expresion.length) {
				this.expresionPila.apilar(expresion[contador]);
			} else {
				this.expresionPila.apilar(")");
			}
			// Inicia Resolucion si encuentra ")"
			if (cierraParentesis()) {
				operando = resuelveParentesis(operando);
			}
		}
		// Almacena Resultado
		setResultado(new Double(this.expresionPila.tope()));
	}

	/**
	 * @return si devuelve o no ")"
	 */
	private boolean cierraParentesis() {
		return this.expresionPila.tope().equals(")");
	}

	/**
	 * Post: Almacena resultado de la ecuacion
	 * 
	 * @param resultado
	 */
	private void setResultado(Double resultado) {
		this.resultado = resultado;
	}

	/**
	 * @param operando
	 * @return
	 */
	private double resuelveParentesis(double operando) {
		this.expresionPila.desapilar(); // Desapila ")"
		while (!this.expresionPila.tope().equals("(")) {
			switch (this.expresionPila.tope()) {
			case "+":
				suma(operando);
				break;
			case "*":
				producto(operando);
				break;
			case "-":
				resta(operando);
				break;
			case "/":
				division(operando);
				break;
			default:
				operando = setOperando();
			}

		}
		// Reemplaza: el numero anterior por el resultado
		// de la operacion
		this.expresionPila.desapilar();
		this.expresionPila.apilar(Double.toString(operando));
		return operando;
	}

	/**
	 * @return Devuelve operando desapilado
	 */
	private double setOperando() {
		double operando;
		operando = new Double(this.expresionPila.tope());
		this.expresionPila.desapilar();
		System.out.println("[Info@evaluar] Se almacena Variable: " + operando);
		return operando;
	}

	/**
	 * Post: Numero queda dividido
	 * 
	 * @param operando
	 *            Numero a dividir
	 */
	private void division(double operando) {
		double resultado = 0.0;
		this.expresionPila.desapilar();

		String numeroDelTope = this.expresionPila.tope();
		if (operando != 0) {
			resultado = new Double(numeroDelTope) / operando;

			System.out.println("[Info@evaluar] Division: "
					+ this.expresionPila.tope() + " / " + operando + " = "
					+ resultado);
			this.expresionPila.desapilar();
			this.expresionPila.apilar((Double.toString(resultado)));
		} else {
			IllegalArgumentException errorDivisionPorCero = new IllegalArgumentException(
					"NO se puede dividir por cero");
			throw errorDivisionPorCero;
		}
	}

	/**
	 * Post: Numero queda restado
	 * 
	 * @param operando
	 *            Numero a restar
	 */
	private void resta(double operando) {
		double resultado;
		this.expresionPila.desapilar();
		resultado = new Double(this.expresionPila.tope()) - operando;
		System.out.println("[Info@evaluar] Resta: " + this.expresionPila.tope()
				+ " - " + operando + " = " + resultado);
		this.expresionPila.desapilar();
		this.expresionPila.apilar((Double.toString(resultado)));
	}

	/**
	 * Post: Numero queda multiplicado
	 * 
	 * @param operando
	 *            Numero a multiplicar
	 */
	private void producto(double operando) {
		double resultado;
		this.expresionPila.desapilar();
		resultado = new Double(this.expresionPila.tope()) * operando;
		System.out.println("[Info@evaluar] Producto: " + operando + " * "
				+ this.expresionPila.tope() + " = " + resultado);
		this.expresionPila.desapilar();
		this.expresionPila.apilar((Double.toString(resultado)));
	}

	/**
	 * Post: Numero queda Sumado
	 * 
	 * @param operando
	 *            Numero a sumar
	 */
	private void suma(double operando) {
		double resultado;
		this.expresionPila.desapilar();
		resultado = new Double(this.expresionPila.tope()) + operando;
		System.out.println("[Info@evaluar] Suma: " + operando + " + "
				+ this.expresionPila.tope() + " = " + resultado);
		this.expresionPila.desapilar();
		this.expresionPila.apilar((Double.toString(resultado)));
	}

	/**
	 * @return Resultado de calculo
	 */
	public double getResultado() {
		System.out.println("[Info@getResultado] Resultado de la ecuacion: "
				+ this.resultado);
		return this.resultado;
	}
}
